﻿using System;
using System.Linq;
using System.Timers;
using System.Windows.Media.Imaging;

namespace Schuifpuzzel.Model
{
     public enum MoveDirection
        {
            Up =0,
            Down=1,
            Left=2,
            Right=3
        }

    public delegate void PropertyChanged();

    /// <summary>
    /// This class contains all the logic for a 3x3 slide puzzle
    /// </summary>
    public class SlidePuzzle
    {

        private const int Shufflemoves = 1000; //The amount of times to make a random move when we shuffle the puzzle
        public event PropertyChanged PropertyChanged;
        
        private int[] _puzzle;
        private int _originalEmptyTile;
        private int _currentEmptyTile;

        private Random _random = new Random();
        private int _timeElapsed;
        private int _moves;
        private Timer _timer;

        /// <summary>
        /// The amount of time elapsed while playing the puzzle
        /// </summary>
        public int TimeElapsed
        {
            get { return _timeElapsed; }
            set
            {
                _timeElapsed = value;
                if (PropertyChanged != null)
                { PropertyChanged();
                }
            }
        }
        /// <summary>
        /// Amount of moves made while playing the puzzle
        /// </summary>
        public int Moves
        {
            get { return _moves; }
            set
            {
                _moves = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged();
                }
            }
        }

        /// <summary>
        /// Get the number of the tile located at a specific location
        /// </summary>
        /// <param name="tileLocation">Location for which we wish to know which tile it contains</param>
        /// <returns></returns>
        public int GetTileNumber(int tileLocation)
        {
            return _puzzle[tileLocation];
        }
        


        public SlidePuzzle(int emptyTile)
        {
            _puzzle = new int[9];
            _originalEmptyTile = emptyTile;
            _currentEmptyTile = emptyTile;
            ResetPuzzle();

            _timer = new Timer(1000);
            _timer.Elapsed += TimerElapsed;
            _timer.Enabled = true;
        }

       
        /// <summary>
        /// Reset the puzzle to starting position
        /// </summary>
        public void ResetPuzzle()
        {
            for (int i = 0; i < _puzzle.Length; i++)
            {
                _puzzle[i] = i;
            }
            _currentEmptyTile = _originalEmptyTile;
        }

        /// <summary>
        /// Start the puzzle
        /// </summary>
        public void StartPuzzle()
        {
            Shuffle();

            TimeElapsed = 0;
            Moves = 0;
            _timer.Start();                              // Start the timer
        }

        /// <summary>
        /// Shuffle the puzzlepieces
        /// </summary>
        private void Shuffle()
        {
            for (int i = 0; i < Shufflemoves; i++)
            {
                MoveEmptyTile((MoveDirection)_random.Next(0, 4));
            }
        }

        /// <summary>
        /// Try to move the empty tile into a certain direction
        /// </summary>
        /// <param name="direction">The direction you wish to move the empty tile to</param>
        /// <returns></returns>
        public Boolean MoveEmptyTile(MoveDirection direction)
        {
            int tileToMove = -1;
            Boolean isSolved;
            switch (direction)
            {
                    
                case MoveDirection.Up:
                    tileToMove = _currentEmptyTile +3;
                    break;
                case MoveDirection.Down:
                    tileToMove = _currentEmptyTile - 3;
                    break;
                case MoveDirection.Left:
                    if (!(_currentEmptyTile == 2 || _currentEmptyTile == 5 || _currentEmptyTile == 8))
                    {
                        tileToMove = _currentEmptyTile +1;
                    }
                    break;
                case MoveDirection.Right:
                    if (!(_currentEmptyTile == 0 || _currentEmptyTile == 3 || _currentEmptyTile == 6))
                    {
                        tileToMove = _currentEmptyTile - 1;
                    }
                        break;
            }

            if (tileToMove >= 0 && tileToMove <= 8)
            {
                SwitchTiles(tileToMove, _currentEmptyTile);
                Moves += 1;
                isSolved = CheckIfSolved();
                if (isSolved)
                {
                    _timer.Stop();
                }
                return isSolved;
            }
            return false;
        }

        /// <summary>
        /// Check if the possible is solved
        /// </summary>
        /// <returns></returns>
        private Boolean CheckIfSolved()
        {
            return !_puzzle.Where((t, i) => t != i).Any();
        }

        /// <summary>
        /// Switches the location of two tiles
        /// </summary>
        /// <param name="location1"></param>
        /// <param name="location2"></param>
        private void SwitchTiles(int location1, int location2)
        {
            if (location1 == _currentEmptyTile)
            {
                _currentEmptyTile = location2;
            }
            else if (location2 == _currentEmptyTile)
            {
                _currentEmptyTile = location1;
            }

            int temp = _puzzle[location1];

            _puzzle[location1] = _puzzle[location2];
            _puzzle[location2] = temp;
        }

        /// <summary>
        /// Adds a second to the player's time
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            TimeElapsed += 1;
        }
    }
}
